import os
import time
import json
import threading
import requests
import logging
from datetime import datetime, timedelta
from flask import current_app

# 创建日志记录器
logger = logging.getLogger("wx_token")


class WxTokenManager:
    """微信小程序 access_token 管理器"""

    _instance = None
    _lock = threading.Lock()

    def __new__(cls):
        """单例模式实现"""
        with cls._lock:
            if cls._instance is None:
                cls._instance = super(WxTokenManager, cls).__new__(cls)
                cls._instance._initialized = False
            return cls._instance

    def __init__(self):
        """初始化token管理器"""
        if self._initialized:
            return

        self._token = None
        self._token_expire_time = None
        self._appid = None
        self._secret = None
        self._token_file = None
        self._app = None
        self._initialized = True

    def init_app(self, app):
        """使用Flask应用配置初始化token管理器"""
        self._app = app
        self._appid = app.config.get("WX_APPID")
        self._secret = app.config.get("WX_SECRET")

        # 设置日志级别
        log_level = app.config.get("LOG_LEVEL", "INFO")
        numeric_level = getattr(logging, log_level.upper(), None)
        if isinstance(numeric_level, int):
            logger.setLevel(numeric_level)

        # 尝试从配置中获取已存在的token (用于开发环境)
        self._token = app.config.get("WX_AUTH_TOKEN")
        if self._token:
            # 如果有固定token，设置一个较长的过期时间
            self._token_expire_time = datetime.now() + timedelta(days=30)
            logger.info("使用配置文件中的固定WX_AUTH_TOKEN")

        # 设置token文件路径
        self._token_file = os.path.join(app.instance_path, "wx_token.json")

        # 打印配置信息
        if app.debug:
            logger.info(f"微信配置 - APPID: {self._appid or '未设置'}")
            logger.info(f"微信配置 - SECRET: {'已设置' if self._secret else '未设置'}")
            logger.info(
                f"微信配置 - 预设TOKEN: {'已设置' if self._token else '未设置'}"
            )

        # 确保instance目录存在
        os.makedirs(app.instance_path, exist_ok=True)

        # 如果没有固定token，则尝试从文件加载
        if not self._token:
            if self._load_token():
                logger.info(f"从文件加载token成功，过期时间: {self._token_expire_time}")
            else:
                logger.info("没有可用的token，需要时将从微信API获取")

    def _load_token(self):
        """从文件加载token"""
        if not os.path.exists(self._token_file):
            return False

        try:
            with open(self._token_file, "r") as f:
                data = json.load(f)
                self._token = data.get("access_token")
                expire_time = data.get("expire_time")
                if expire_time:
                    self._token_expire_time = datetime.fromisoformat(expire_time)
                return True
        except (json.JSONDecodeError, IOError) as e:
            logger.error(f"加载access_token失败: {e}")
            return False

    def _save_token(self):
        """保存token到文件"""
        data = {
            "access_token": self._token,
            "expire_time": (
                self._token_expire_time.isoformat() if self._token_expire_time else None
            ),
        }

        try:
            with open(self._token_file, "w") as f:
                json.dump(data, f)
            logger.debug(f"Token保存成功，过期时间: {self._token_expire_time}")
        except IOError as e:
            logger.error(f"保存access_token失败: {e}")

    def _fetch_new_token(self):
        """从微信API获取新token"""
        if not self._appid or not self._secret:
            logger.error("未配置WX_APPID或WX_SECRET")
            return False

        url = f"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={self._appid}&secret={self._secret}"
        logger.debug(f"准备请求新的access_token: {url}")

        try:
            response = requests.get(url)
            data = response.json()

            if "access_token" in data:
                self._token = data["access_token"]
                # 设置过期时间，提前5分钟过期以确保安全
                expires_in = data.get("expires_in", 7200)
                self._token_expire_time = datetime.now() + timedelta(
                    seconds=expires_in - 300
                )
                self._save_token()
                # 仅显示token的前10个字符，保护安全
                safe_token = self._token[:10] + "..." if self._token else "None"
                logger.info(f"成功获取微信access_token: {safe_token}")
                return True
            else:
                error_msg = data.get("errmsg", "未知错误")
                error_code = data.get("errcode", -1)
                logger.error(
                    f"获取access_token失败: 错误码={error_code}, 信息={error_msg}"
                )
                return False

        except Exception as e:
            logger.error(f"获取access_token时发生错误: {e}")
            return False

    def get_access_token(self, force_refresh=False):
        """获取有效的access_token

        Args:
            force_refresh: 是否强制刷新token

        Returns:
            str: access_token或None（如果获取失败）
        """
        with self._lock:
            # 检查是否需要刷新token
            if (
                force_refresh
                or not self._token
                or not self._token_expire_time
                or datetime.now() >= self._token_expire_time
            ):
                if not self._fetch_new_token():
                    return None

            # 打印当前token状态
            now = datetime.now()
            if self._token_expire_time:
                remaining = (self._token_expire_time - now).total_seconds() / 60
                logger.debug(f"当前token剩余有效时间: {remaining:.1f}分钟")

            return self._token

    def get_config_status(self):
        """获取配置状态信息，用于命令行显示"""
        return {
            "appid": self._appid or "未设置",
            "has_secret": bool(self._secret),
            "has_token": bool(self._token),
            "token_expires_at": self._token_expire_time,
            "token_file": self._token_file,
        }


# 创建全局单例实例
token_manager = WxTokenManager()
